package org.xtext.runtime.formula;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.xtext.runtime.term.ITerm;
import org.xtext.runtime.term.Variable;
import org.xtext.runtime.util.Bindings;
import org.xtext.runtime.util.ILogicVisitor;

public class ACREASTRAEvent implements Event {
	public static final String STARTED = "started";
	public static final String ADVANCED = "advanced";
	public static final String UNMATCHED = "unmatched";
	public static final String AMBIGUOUS= "ambiguous";
	public static final String ENDED = "ended";
	public static final String FAILED = "failed";
	public static final String CANCEL_REQUEST = "cancel_request";
	public static final String CANCEL_FAIL = "cancel_fail";
	public static final String CANCEL_CONFIRM = "cancel_confirm";
	public static final String TIMEOUT = "timeout";
	public static final String MESSAGE = "message";
	
//	@acre(ended, string cid)
//	@acre(failed, string cid)
//	@acre(cancel-request, string cid)
//	@acre(cancel-fail, string cid)
//	@acre(cancel-confirm, string cid)
//	@acre(timeout, string cid)
	
	String type;
	ITerm conversationId;
	ITerm state;
	ITerm sender;
	ITerm length;
	String performative;
	Predicate content;
	
	public ACREASTRAEvent(String type, ITerm conversationId) {
	   // this is used quite hackishly elsewhere
	   // for ambiguous and unmatched messages, the second parameter is the 'description' string
	   // and is not a conversation identifier
	   this.type=type;
		this.conversationId = conversationId;
	}
	
	public ACREASTRAEvent(String type, ITerm cid, ITerm state, ITerm length) {
		this.type=type;
		this.conversationId = cid;
		this.state = state;
		this.length = length;
	}

	public ACREASTRAEvent(String type, ITerm conversationId, String performative, Predicate content) {
	   this.type=type;
	   this.conversationId = conversationId;
	   this.performative = performative;
	   this.content = content;
	}
	
	@Override
	public ACREASTRAEvent copy() {
		if (type.equals(ACREASTRAEvent.ADVANCED)) {
			return new ACREASTRAEvent(type, conversationId.copy(), state.copy(), length.copy());
		} /*else if (type.equals(UNMATCHED) || type.equals(AMBIGUOUS)) {
			return new ACREASTRAEvent(type, performative, sender.copy(), content.copy());
		}*/ else if (type.equals(MESSAGE)) {
		   return new ACREASTRAEvent(type,conversationId.copy(),performative,content.copy());
		}
		return new ACREASTRAEvent(type, conversationId.copy());
	}

	@Override
	public Set<Variable> variables() {
		Set<Variable> variables = new HashSet<Variable>();
		
		if (type.equals(ACREASTRAEvent.ADVANCED)) {
			if (conversationId instanceof Variable) {
				variables.add((Variable) conversationId); 
			}
			if (state instanceof Variable) {
				variables.add((Variable) state); 
			}
			if (length instanceof Variable) {
				variables.add((Variable) length); 
			}
		} /*else if (type.equals(UNMATCHED) || type.equals(AMBIGUOUS)) {
			if (sender instanceof Variable) {
				variables.add((Variable) sender); 
			}
			variables.addAll(content.variables());
		}*/ else if (type.equals(MESSAGE)) {
		   if (conversationId instanceof Variable) {
		      variables.add((Variable) conversationId);
		   }
		   variables.addAll(content.variables());
		} else {
			if (conversationId instanceof Variable) {
				variables.add((Variable) conversationId); 
			}
		}
		return variables;
	}

	@Override
	public ACREASTRAEvent apply(Bindings bindings) {
		if (type.equals(ACREASTRAEvent.ADVANCED)) {
			conversationId = conversationId.apply(bindings);
			state = state.apply(bindings);
			length = length.apply(bindings);
		} /*else if (type.equals(UNMATCHED) || type.equals(AMBIGUOUS)) {
			sender = sender.apply(bindings);
			content = content.apply(bindings);
		} */else if (type.equals(MESSAGE)) {
		   conversationId = conversationId.apply(bindings);
		   content = content.apply(bindings);
		} else {
			conversationId = conversationId.apply(bindings);
		}
		
		return this;
	}

	@Override
	public ACREASTRAEvent copy(Map<String, Variable> vars) {
		if (type.equals(ACREASTRAEvent.ADVANCED)) {
			return new ACREASTRAEvent(type, conversationId.copy(vars), state.copy(vars), length.copy(vars));
		} /*else if (type.equals(UNMATCHED) || type.equals(AMBIGUOUS)) {
			return new ACREASTRAEvent(type, performative, sender.copy(vars), content.copy(vars));
		} */else if (type.equals(MESSAGE)) {
		   return new ACREASTRAEvent(type,conversationId.copy(vars), performative, content.copy(vars));
		}
		return new ACREASTRAEvent(type, conversationId.copy(vars));
	}

	@Override
	public boolean hasFreeVariables() {
		if (type.equals(ADVANCED)) {
			return conversationId.hasFreeVariables() && state.hasFreeVariables() && length.hasFreeVariables();
		} /*else if (type.equals(UNMATCHED) || type.equals(AMBIGUOUS)) {
			return sender.hasFreeVariables() && content.hasFreeVariables();
		} */else if (type.equals(MESSAGE)) {
		   return conversationId.hasFreeVariables() && content.hasFreeVariables();
		}
		
		return conversationId.hasFreeVariables();
	}

	@Override
	public Object accept(ILogicVisitor visitor) {
		return null;
	}

	@Override
	public String signature() {
		return "@acre:"+type;
	}

	@Override
	public String toString() {

	      StringBuilder toReturn = new StringBuilder( "@acre(" );
	      if ( this.type.equals( ADVANCED ) ) {
	         toReturn.append( this.type );
	         toReturn.append( ", " + this.conversationId.toString() );
	         toReturn.append( ", " + this.state.toString() );
	         toReturn.append(  ", " + this.length.toString() );
	      }
	      else if ( type.equals( MESSAGE ) ) {
	         toReturn.append( this.type );
	         toReturn.append( ", " + this.conversationId.toString() );
	         toReturn.append( ", " + this.performative );
	         toReturn.append( ", " + this.content.toString() );
	      }
	      else {
	         toReturn.append( this.type + ", "  + this.conversationId.toString() );
	      }
	      toReturn.append( ")" );
	      return toReturn.toString();
	}
	
	@Override
	public Object source() {
		return null;
	}

	public String type() {
		return type;
	}

	public ITerm conversationId() {
		return conversationId;
	}

	public ITerm state() {
		return state;
	}
	
	public ITerm length() {
		return length;
	}
	
	public Predicate content() {
	   return content;
	}

	public String performative() {
	   return performative;
	}
}
